Conditions | 115 |
Total Lines | 786 |
Code Lines | 424 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like egw_action_popup.js ➔ egwPopupActionImplementation often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | /** |
||
133 | function egwPopupActionImplementation() |
||
134 | { |
||
135 | var ai = new egwActionImplementation(); |
||
136 | |||
137 | ai.type = "popup"; |
||
138 | |||
139 | ai.auto_paste = true; |
||
140 | |||
141 | /** |
||
142 | * Registers the handler for the default action |
||
143 | * |
||
144 | * @param {DOMNode} _node |
||
145 | * @param {function} _callback |
||
146 | * @param {object} _context |
||
147 | * @returns {boolean} |
||
148 | */ |
||
149 | ai._registerDefault = function(_node, _callback, _context) |
||
150 | { |
||
151 | var defaultHandler = function(e) { |
||
152 | // Prevent bubbling bound event on <a> tag, on touch devices |
||
153 | // a tag should be handled by default event |
||
154 | if (egwIsMobile() && e.target.tagName == "A") return true; |
||
155 | |||
156 | if (typeof document.selection != "undefined" && typeof document.selection.empty != "undefined") |
||
157 | { |
||
158 | document.selection.empty(); |
||
159 | } |
||
160 | else if( typeof window.getSelection != "undefined") |
||
161 | { |
||
162 | var sel = window.getSelection(); |
||
163 | sel.removeAllRanges(); |
||
164 | } |
||
165 | |||
166 | _callback.call(_context, "default", ai); |
||
167 | |||
168 | // Stop action from bubbling up to parents |
||
169 | e.stopPropagation(); |
||
170 | e.cancelBubble = true; |
||
171 | |||
172 | // remove context menu if we are in mobile theme |
||
173 | // and intended to open the entry |
||
174 | if (_egw_active_menu && e.which == 1) _egw_active_menu.hide(); |
||
175 | return false; |
||
176 | }; |
||
177 | |||
178 | if (egwIsMobile()) { |
||
179 | jQuery(_node).bind('click', defaultHandler); |
||
180 | } else { |
||
181 | _node.ondblclick = defaultHandler; |
||
182 | } |
||
183 | }; |
||
184 | |||
185 | ai._getDefaultLink = function(_links) { |
||
186 | var defaultAction = null; |
||
187 | for (var k in _links) |
||
188 | { |
||
189 | if (_links[k].actionObj["default"] && _links[k].enabled) |
||
190 | { |
||
191 | defaultAction = _links[k].actionObj; |
||
192 | break; |
||
193 | } |
||
194 | } |
||
195 | |||
196 | return defaultAction; |
||
197 | }; |
||
198 | |||
199 | ai._searchShortcut = function (_key, _objs, _links) { |
||
200 | for (var i = 0; i < _objs.length; i++) |
||
201 | { |
||
202 | var sc = _objs[i].shortcut; |
||
203 | if (sc && sc.keyCode == _key.keyCode && sc.shift == _key.shift && |
||
204 | sc.ctrl == _key.ctrl && sc.alt == _key.alt && |
||
205 | _objs[i].type == "popup" && (typeof _links[_objs[i].id] == "undefined" || |
||
206 | _links[_objs[i].id].enabled)) |
||
207 | { |
||
208 | return _objs[i]; |
||
209 | } |
||
210 | |||
211 | var obj = this._searchShortcut(_key, _objs[i].children, _links); |
||
212 | if (obj) { |
||
213 | return obj; |
||
214 | } |
||
215 | } |
||
216 | }; |
||
217 | |||
218 | ai._searchShortcutInLinks = function(_key, _links) { |
||
219 | var objs = []; |
||
220 | for (var k in _links) |
||
221 | { |
||
222 | if (_links[k].enabled) |
||
223 | { |
||
224 | objs.push(_links[k].actionObj); |
||
225 | } |
||
226 | } |
||
227 | |||
228 | return ai._searchShortcut(_key, objs, _links); |
||
229 | }; |
||
230 | |||
231 | /** |
||
232 | * Handles a key press |
||
233 | * |
||
234 | * @param {object} _key |
||
235 | * @param {type} _selected |
||
236 | * @param {type} _links |
||
237 | * @param {type} _target |
||
238 | * @returns {Boolean} |
||
239 | */ |
||
240 | ai._handleKeyPress = function(_key, _selected, _links, _target) { |
||
241 | // Handle the default |
||
242 | if (_key.keyCode == EGW_KEY_ENTER && !_key.ctrl && !_key.shift && !_key.alt) { |
||
243 | var defaultAction = this._getDefaultLink(_links); |
||
244 | if (defaultAction) |
||
245 | { |
||
246 | defaultAction.execute(_selected); |
||
247 | return true; |
||
248 | } |
||
249 | } |
||
250 | |||
251 | // Menu button |
||
252 | if (_key.keyCode == EGW_KEY_MENU && !_key.ctrl) |
||
253 | { |
||
254 | return this.doExecuteImplementation({posx:0,posy:0}, _selected, _links, _target); |
||
255 | } |
||
256 | |||
257 | |||
258 | // Check whether the given shortcut exists |
||
259 | var obj = this._searchShortcutInLinks(_key, _links); |
||
260 | if (obj) |
||
261 | { |
||
262 | obj.execute(_selected); |
||
263 | return true; |
||
264 | } |
||
265 | |||
266 | return false; |
||
267 | }; |
||
268 | |||
269 | /** |
||
270 | * Registers the handler for the context menu |
||
271 | * |
||
272 | * @param {DOMNode} _node |
||
273 | * @param {function} _callback |
||
274 | * @param {object} _context |
||
275 | * @returns {boolean} |
||
276 | */ |
||
277 | ai._registerContext = function(_node, _callback, _context) |
||
278 | { |
||
279 | var contextHandler = function(e) { |
||
280 | if(egwIsMobile()) |
||
281 | { |
||
282 | if (e.originalEvent.which == 3) |
||
283 | { |
||
|
|||
284 | // Enable onhold trigger till we define a better handler for tree contextmenu |
||
285 | // return; |
||
286 | } |
||
287 | } |
||
288 | |||
289 | //Obtain the event object |
||
290 | if (!e) |
||
291 | { |
||
292 | e = window.event; |
||
293 | } |
||
294 | |||
295 | if (_egw_active_menu) |
||
296 | { |
||
297 | _egw_active_menu.hide(); |
||
298 | } |
||
299 | else if (!e.ctrlKey && e.which == 3 || e.which === 0) // tap event indicates by 0 |
||
300 | { |
||
301 | var _xy = ai._getPageXY(e); |
||
302 | var _implContext = {event:e, posx:_xy.posx, posy: _xy.posy}; |
||
303 | _callback.call(_context, _implContext, ai); |
||
304 | } |
||
305 | |||
306 | e.cancelBubble = !e.ctrlKey || e.which == 1; |
||
307 | if (e.stopPropagation && e.cancelBubble) |
||
308 | { |
||
309 | e.stopPropagation(); |
||
310 | } |
||
311 | return !e.cancelBubble; |
||
312 | }; |
||
313 | // Safari still needs the taphold to trigger contextmenu |
||
314 | // Chrome has default event on touch and hold which acts like right click |
||
315 | jQuery(_node).bind('taphold', contextHandler); |
||
316 | jQuery(_node).on('contextmenu', contextHandler); |
||
317 | }; |
||
318 | |||
319 | ai.doRegisterAction = function(_aoi, _callback, _context) |
||
320 | { |
||
321 | var node = _aoi.getDOMNode(); |
||
322 | |||
323 | if (node) |
||
324 | { |
||
325 | this._registerDefault(node, _callback, _context); |
||
326 | this._registerContext(node, _callback, _context); |
||
327 | return true; |
||
328 | } |
||
329 | return false; |
||
330 | }; |
||
331 | |||
332 | ai.doUnregisterAction = function(_aoi) |
||
333 | { |
||
334 | // |
||
335 | }; |
||
336 | |||
337 | /** |
||
338 | * Builds the context menu and shows it at the given position/DOM-Node. |
||
339 | * |
||
340 | * @param {object} _context |
||
341 | * @param {type} _selected |
||
342 | * @param {type} _links |
||
343 | * @param {type} _target |
||
344 | * @returns {Boolean} |
||
345 | */ |
||
346 | ai.doExecuteImplementation = function(_context, _selected, _links, _target) |
||
347 | { |
||
348 | if (typeof _target == "undefined") |
||
349 | { |
||
350 | _target = null; |
||
351 | } |
||
352 | |||
353 | ai._context = _context; |
||
354 | if (typeof _context == "object" && typeof _context.keyEvent == "object") |
||
355 | { |
||
356 | return ai._handleKeyPress(_context.keyEvent, _selected, _links, _target); |
||
357 | } |
||
358 | else if (_context != "default") |
||
359 | { |
||
360 | //Check whether the context has the posx and posy parameters |
||
361 | if ((typeof _context.posx != "number" || typeof _context.posy != "number") && |
||
362 | typeof _context.id != "undefined") |
||
363 | { |
||
364 | // Calculate context menu position from the given DOM-Node |
||
365 | var node = _context; |
||
366 | |||
367 | x = jQuery(node).offset().left; |
||
368 | y = jQuery(node).offset().top; |
||
369 | |||
370 | _context = {"posx": x, "posy": y}; |
||
371 | } |
||
372 | |||
373 | var menu = ai._buildMenu(_links, _selected, _target); |
||
374 | menu.showAt(_context.posx, _context.posy); |
||
375 | |||
376 | return true; |
||
377 | } |
||
378 | else |
||
379 | { |
||
380 | var defaultAction = ai._getDefaultLink(_links); |
||
381 | if (defaultAction) |
||
382 | { |
||
383 | defaultAction.execute(_selected); |
||
384 | } |
||
385 | } |
||
386 | |||
387 | return false; |
||
388 | }; |
||
389 | |||
390 | /** |
||
391 | * Groups and sorts the given action tree layer |
||
392 | * |
||
393 | * @param {type} _layer |
||
394 | * @param {type} _links |
||
395 | * @param {type} _parentGroup |
||
396 | */ |
||
397 | ai._groupLayers = function(_layer, _links, _parentGroup) |
||
398 | { |
||
399 | // Seperate the multiple groups out of the layer |
||
400 | var link_groups = {}; |
||
401 | |||
402 | for (var i = 0; i < _layer.children.length; i++) |
||
403 | { |
||
404 | var actionObj = _layer.children[i].action; |
||
405 | |||
406 | // Check whether the link group of the current element already exists, |
||
407 | // if not, create the group |
||
408 | var grp = actionObj.group; |
||
409 | if (typeof link_groups[grp] == "undefined") |
||
410 | { |
||
411 | link_groups[grp] = []; |
||
412 | } |
||
413 | |||
414 | // Search the link data for this action object if none is found, |
||
415 | // visible and enabled = true is assumed |
||
416 | var visible = true; |
||
417 | var enabled = true; |
||
418 | |||
419 | if (typeof _links[actionObj.id] != "undefined") |
||
420 | { |
||
421 | visible = _links[actionObj.id].visible; |
||
422 | enabled = _links[actionObj.id].enabled; |
||
423 | } |
||
424 | |||
425 | // Insert the element in order |
||
426 | var inserted = false; |
||
427 | var groupObj = { |
||
428 | "actionObj": actionObj, |
||
429 | "visible": visible, |
||
430 | "enabled": enabled, |
||
431 | "groups": [] |
||
432 | }; |
||
433 | |||
434 | for (var j = 0; j < link_groups[grp].length; j++) |
||
435 | { |
||
436 | var elem = link_groups[grp][j].actionObj; |
||
437 | if (elem.order > actionObj.order) |
||
438 | { |
||
439 | inserted = true; |
||
440 | link_groups[grp].splice(j, 0, groupObj); |
||
441 | break; |
||
442 | } |
||
443 | } |
||
444 | |||
445 | // If the object hasn't been inserted, add it to the end of the list |
||
446 | if (!inserted) |
||
447 | { |
||
448 | link_groups[grp].push(groupObj); |
||
449 | } |
||
450 | |||
451 | // If this child itself has children, group those elements too |
||
452 | if (_layer.children[i].children.length > 0) |
||
453 | { |
||
454 | this._groupLayers(_layer.children[i], _links, groupObj); |
||
455 | } |
||
456 | } |
||
457 | |||
458 | // Transform the link_groups object into an sorted array |
||
459 | var groups = []; |
||
460 | |||
461 | for (var k in link_groups) |
||
462 | { |
||
463 | groups.push({"grp": k, "links": link_groups[k]}); |
||
464 | } |
||
465 | |||
466 | groups.sort(function(a, b) { |
||
467 | var ia = parseInt(a.grp); |
||
468 | var ib = parseInt(b.grp); |
||
469 | return (ia > ib) ? 1 : ((ia < ib) ? -1 : 0); |
||
470 | }); |
||
471 | |||
472 | // Append the groups to the groups2 array |
||
473 | var groups2 = []; |
||
474 | for (var i = 0; i < groups.length; i++) |
||
475 | { |
||
476 | groups2.push(groups[i].links); |
||
477 | } |
||
478 | |||
479 | _parentGroup.groups = groups2; |
||
480 | }; |
||
481 | |||
482 | /** |
||
483 | * Build the menu layers |
||
484 | * |
||
485 | * @param {type} _menu |
||
486 | * @param {type} _groups |
||
487 | * @param {type} _selected |
||
488 | * @param {type} _enabled |
||
489 | * @param {type} _target |
||
490 | */ |
||
491 | ai._buildMenuLayer = function(_menu, _groups, _selected, _enabled, _target) |
||
492 | { |
||
493 | var firstGroup = true; |
||
494 | |||
495 | for (var i = 0; i < _groups.length; i++) |
||
496 | { |
||
497 | var firstElem = true; |
||
498 | |||
499 | // Go through the elements of each group |
||
500 | for (var j = 0; j < _groups[i].length; j++) |
||
501 | { |
||
502 | var link = _groups[i][j]; |
||
503 | |||
504 | if (link.visible) |
||
505 | { |
||
506 | // Add an seperator after each group |
||
507 | if (!firstGroup && firstElem) |
||
508 | { |
||
509 | _menu.addItem("", "-"); |
||
510 | } |
||
511 | firstElem = false; |
||
512 | |||
513 | var item = _menu.addItem(link.actionObj.id, link.actionObj.caption, |
||
514 | link.actionObj.iconUrl); |
||
515 | item["default"] = link.actionObj["default"]; |
||
516 | |||
517 | // As this code is also used when a drag-drop popup menu is built, |
||
518 | // we have to perform this check |
||
519 | if (link.actionObj.type == "popup") |
||
520 | { |
||
521 | item.set_hint(link.actionObj.hint); |
||
522 | item.set_checkbox(link.actionObj.checkbox); |
||
523 | item.set_checked(link.actionObj.checked); |
||
524 | if(link.actionObj.checkbox && link.actionObj.isChecked) |
||
525 | { |
||
526 | item.set_checked(link.actionObj.isChecked.exec(link.actionObj, _selected)); |
||
527 | } |
||
528 | item.set_groupIndex(link.actionObj.radioGroup); |
||
529 | |||
530 | if (link.actionObj.shortcut && !egwIsMobile()) |
||
531 | { |
||
532 | var sc = link.actionObj.shortcut; |
||
533 | item.set_shortcutCaption(sc.caption); |
||
534 | } |
||
535 | } |
||
536 | |||
537 | item.set_data(link.actionObj); |
||
538 | if (link.enabled && _enabled) |
||
539 | { |
||
540 | item.set_onClick(function(elem) { |
||
541 | // Pass the context |
||
542 | elem.data.menu_context = ai._context; |
||
543 | |||
544 | // Copy the "checked" state |
||
545 | if (typeof elem.data.checked != "undefined") |
||
546 | { |
||
547 | elem.data.checked = elem.checked; |
||
548 | } |
||
549 | |||
550 | elem.data.execute(_selected, _target); |
||
551 | |||
552 | if (typeof elem.data.checkbox != "undefined" && elem.data.checkbox) |
||
553 | { |
||
554 | return elem.data.checked; |
||
555 | } |
||
556 | }); |
||
557 | } |
||
558 | else |
||
559 | { |
||
560 | item.set_enabled(false); |
||
561 | } |
||
562 | |||
563 | // Append the parent groups |
||
564 | if (link.groups) |
||
565 | { |
||
566 | this._buildMenuLayer(item, link.groups, _selected, link.enabled, _target); |
||
567 | } |
||
568 | } |
||
569 | } |
||
570 | |||
571 | firstGroup = firstGroup && firstElem; |
||
572 | } |
||
573 | }; |
||
574 | |||
575 | /** |
||
576 | * Builds the context menu from the given action links |
||
577 | * |
||
578 | * @param {type} _links |
||
579 | * @param {type} _selected |
||
580 | * @param {type} _target |
||
581 | * @returns {egwMenu|egwActionImplementation._buildMenu.menu} |
||
582 | */ |
||
583 | ai._buildMenu = function(_links, _selected, _target) |
||
584 | { |
||
585 | // Build a tree containing all actions |
||
586 | var tree = {"root": []}; |
||
587 | |||
588 | // Automatically add in Drag & Drop actions |
||
589 | if(this.auto_paste && !egwIsMobile()) |
||
590 | { |
||
591 | this._addCopyPaste(_links,_selected); |
||
592 | } |
||
593 | |||
594 | for (var k in _links) |
||
595 | { |
||
596 | _links[k].actionObj.appendToTree(tree); |
||
597 | } |
||
598 | |||
599 | // We need the dummy object container in order to pass the array by |
||
600 | // reference |
||
601 | var groups = { |
||
602 | "groups": [] |
||
603 | }; |
||
604 | |||
605 | if (tree.root.length > 0) |
||
606 | { |
||
607 | // Sort every action object layer by the given sort position and grouping |
||
608 | this._groupLayers(tree.root[0], _links, groups); |
||
609 | } |
||
610 | |||
611 | var menu = new egwMenu(); |
||
612 | |||
613 | // Build the menu layers |
||
614 | this._buildMenuLayer(menu, groups.groups, _selected, true, _target); |
||
615 | |||
616 | return menu; |
||
617 | }; |
||
618 | |||
619 | ai._getPageXY = function getPageXY(event) |
||
620 | { |
||
621 | // document.body.scrollTop does not work in IE |
||
622 | var scrollTop = document.body.scrollTop ? document.body.scrollTop : |
||
623 | document.documentElement.scrollTop; |
||
624 | var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : |
||
625 | document.documentElement.scrollLeft; |
||
626 | |||
627 | return {'posx': (event.clientX + scrollLeft), 'posy': (event.clientY + scrollTop)}; |
||
628 | }; |
||
629 | |||
630 | /** |
||
631 | * Automagically add in context menu items for copy and paste from |
||
632 | * drag and drop actions, based on current clipboard and the accepted types |
||
633 | * |
||
634 | * @param {object[]} _links Actions for inclusion in the menu |
||
635 | * @param {egwActionObject[]} _selected Currently selected entries |
||
636 | */ |
||
637 | ai._addCopyPaste = function (_links, _selected) |
||
638 | { |
||
639 | // Get a list of drag & drop actions |
||
640 | var drag = _selected[0].getSelectedLinks('drag').links; |
||
641 | var drop = _selected[0].getSelectedLinks('drop').links; |
||
642 | |||
643 | // No drags & no drops means early exit |
||
644 | if((!drag || jQuery.isEmptyObject(drag)) && (!drop || jQuery.isEmptyObject(drop))) |
||
645 | { |
||
646 | return; |
||
647 | } |
||
648 | |||
649 | // Find existing actions so we don't get copies |
||
650 | var mgr = _selected[0].manager; |
||
651 | var copy_action = mgr.getActionById('egw_copy'); |
||
652 | var add_action = mgr.getActionById('egw_copy_add'); |
||
653 | var clipboard_action = mgr.getActionById('egw_os_clipboard'); |
||
654 | var paste_action = mgr.getActionById('egw_paste'); |
||
655 | |||
656 | // Fake UI so we can simulate the position of the drop |
||
657 | var ui = { |
||
658 | position: {top: 0, left: 0}, |
||
659 | offset: {top: 0, left: 0} |
||
660 | }; |
||
661 | if(this._context.event) |
||
662 | { |
||
663 | var event = this._context.event.originalEvent; |
||
664 | ui.position = {top: event.pageY, left: event.pageX}; |
||
665 | ui.offset = {top: event.offsetY, left: event.offsetX}; |
||
666 | } |
||
667 | // Create default copy menu action |
||
668 | if(drag && !jQuery.isEmptyObject(drag)) |
||
669 | { |
||
670 | // Don't re-add if it's there |
||
671 | if(copy_action == null) |
||
672 | { |
||
673 | // Create a drag action that allows linking |
||
674 | copy_action = mgr.addAction('popup', 'egw_copy', egw.lang('Copy to clipboard'), egw.image('copy'), function(action, selected) { |
||
675 | // Copied, now add to clipboard |
||
676 | var clipboard = { |
||
677 | type:[], |
||
678 | selected:[] |
||
679 | }; |
||
680 | |||
681 | // When pasting we need to know the type of drag |
||
682 | for(var k in drag) |
||
683 | { |
||
684 | if(drag[k].enabled && drag[k].actionObj.dragType.length > 0) |
||
685 | { |
||
686 | clipboard.type = clipboard.type.concat(drag[k].actionObj.dragType); |
||
687 | } |
||
688 | } |
||
689 | clipboard.type = jQuery.unique(clipboard.type); |
||
690 | // egwAction is a circular structure and can't be stringified so just take what we want |
||
691 | // Hopefully that's enough for the action handlers |
||
692 | for(var k in selected) |
||
693 | { |
||
694 | if(selected[k].id) clipboard.selected.push({id:selected[k].id, data:selected[k].data}); |
||
695 | } |
||
696 | |||
697 | // Save it in session |
||
698 | egw.setSessionItem('phpgwapi', 'egw_clipboard', JSON.stringify(clipboard)); |
||
699 | },true); |
||
700 | copy_action.group = 2.5; |
||
701 | } |
||
702 | if(add_action == null) |
||
703 | { |
||
704 | // Create an action to add selected to clipboard |
||
705 | add_action = mgr.addAction('popup', 'egw_copy_add', egw.lang('Add to clipboard'), egw.image('copy'), function(action, selected) { |
||
706 | // Copied, now add to clipboard |
||
707 | var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard')) || { |
||
708 | type:[], |
||
709 | selected:[] |
||
710 | }; |
||
711 | |||
712 | // When pasting we need to know the type of drag |
||
713 | for(var k in drag) |
||
714 | { |
||
715 | if(drag[k].enabled && drag[k].actionObj.dragType.length > 0) |
||
716 | { |
||
717 | clipboard.type = clipboard.type.concat(drag[k].actionObj.dragType); |
||
718 | } |
||
719 | } |
||
720 | clipboard.type = jQuery.unique(clipboard.type); |
||
721 | // egwAction is a circular structure and can't be stringified so just take what we want |
||
722 | // Hopefully that's enough for the action handlers |
||
723 | for(var k in selected) |
||
724 | { |
||
725 | if(selected[k].id) clipboard.selected.push({id:selected[k].id, data:selected[k].data}); |
||
726 | } |
||
727 | |||
728 | // Save it in session |
||
729 | egw.setSessionItem('phpgwapi', 'egw_clipboard', JSON.stringify(clipboard)); |
||
730 | },true); |
||
731 | add_action.group = 2.5; |
||
732 | |||
733 | } |
||
734 | if(clipboard_action == null) |
||
735 | { |
||
736 | // Create an action to add selected to clipboard |
||
737 | clipboard_action = mgr.addAction('popup', 'egw_os_clipboard', egw.lang('Copy to OS clipboard'), egw.image('copy'), function(action) { |
||
738 | |||
739 | if(document.queryCommandSupported('copy')) |
||
740 | { |
||
741 | jQuery(action.data.target).trigger('copy'); |
||
742 | } |
||
743 | },true); |
||
744 | clipboard_action.group = 2.5; |
||
745 | } |
||
746 | var os_clipboard_caption = this._context.event.originalEvent.target.innerText.trim(); |
||
747 | clipboard_action.set_caption(egw.lang('Copy "%1"', os_clipboard_caption.length>20 ? os_clipboard_caption.substring(0,20)+'...':os_clipboard_caption)); |
||
748 | clipboard_action.data.target = this._context.event.originalEvent.target; |
||
749 | jQuery(clipboard_action.data.target).off('copy').on('copy', function(event) { |
||
750 | // Cancel any no-select css |
||
751 | var target = jQuery(clipboard_action.data.target); |
||
752 | var old_select = target.css('user-select'); |
||
753 | target.css('user-select','all'); |
||
754 | |||
755 | var range = document.createRange(); |
||
756 | range.selectNode(clipboard_action.data.target); |
||
757 | window.getSelection().removeAllRanges(); |
||
758 | window.getSelection().addRange(range); |
||
759 | |||
760 | target.css('user-select',old_select); |
||
761 | |||
762 | var successful = false; |
||
763 | try { |
||
764 | // detect we are in IE via checking setActive, since it's |
||
765 | // only supported in IE, and make sure there's clipboardData object |
||
766 | if (typeof event.target.setActive !='undefined' && window.clipboardData) |
||
767 | { |
||
768 | window.clipboardData.setData('Text', jQuery(clipboard_action.data.target).text().trim()); |
||
769 | } |
||
770 | if(event.clipboardData) |
||
771 | { |
||
772 | event.clipboardData.setData('text/plain', jQuery(clipboard_action.data.target).text().trim()); |
||
773 | event.clipboardData.setData('text/html', jQuery(clipboard_action.data.target).html()); |
||
774 | } |
||
775 | // Show fail message, just in case |
||
776 | egw.message(egw.lang('Use Ctrl-C/Cmd-C to copy')); |
||
777 | |||
778 | successful = document.execCommand('copy'); |
||
779 | } catch(err) {} |
||
780 | |||
781 | if(successful) |
||
782 | { |
||
783 | // Clear fail message |
||
784 | egw.message(''); |
||
785 | window.getSelection().removeAllRanges(); |
||
786 | target.css('user-select',old_select); |
||
787 | return false; |
||
788 | } |
||
789 | }); |
||
790 | if(typeof _links[copy_action.id] == 'undefined') |
||
791 | { |
||
792 | _links[copy_action.id] = { |
||
793 | "actionObj": copy_action, |
||
794 | "enabled": true, |
||
795 | "visible": true, |
||
796 | "cnt": 0 |
||
797 | }; |
||
798 | } |
||
799 | if(typeof _links[add_action.id] == 'undefined') |
||
800 | { |
||
801 | _links[add_action.id] = { |
||
802 | "actionObj": add_action, |
||
803 | "enabled": true, |
||
804 | "visible": true, |
||
805 | "cnt": 0 |
||
806 | }; |
||
807 | } |
||
808 | if(typeof _links[clipboard_action.id] == 'undefined') |
||
809 | { |
||
810 | _links[clipboard_action.id] = { |
||
811 | "actionObj": clipboard_action, |
||
812 | "enabled": os_clipboard_caption.length > 0, |
||
813 | "visible": os_clipboard_caption.length > 0, |
||
814 | "cnt": 0 |
||
815 | }; |
||
816 | } |
||
817 | } |
||
818 | |||
819 | // Create default paste menu item |
||
820 | if(drop && !jQuery.isEmptyObject(drop)) |
||
821 | { |
||
822 | // Create paste action |
||
823 | // This injects the clipboard data and calls the original handler |
||
824 | var paste_exec = function(action, selected) { |
||
825 | // Add in clipboard as a sender |
||
826 | var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard')); |
||
827 | // Fake drop position |
||
828 | drop[action.id].actionObj.ui = ui; |
||
829 | // Set a flag so apps can tell the difference, if they need to |
||
830 | drop[action.id].actionObj.paste = true; |
||
831 | |||
832 | drop[action.id].actionObj.execute(clipboard.selected,selected[0]); |
||
833 | |||
834 | drop[action.id].actionObj.paste = false; |
||
835 | }; |
||
836 | |||
837 | var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard')) || { |
||
838 | type:[], |
||
839 | selected:[] |
||
840 | }; |
||
841 | |||
842 | // Don't re-add if action already exists |
||
843 | if(paste_action == null) |
||
844 | { |
||
845 | paste_action = mgr.addAction('popup', 'egw_paste', egw.lang('Paste'), egw.image('editpaste'), paste_exec,true); |
||
846 | paste_action.group = 2.5; |
||
847 | paste_action.order = 9; |
||
848 | paste_action.canHaveChildren.push('drop'); |
||
849 | } |
||
850 | |||
851 | // Set hint to something resembling current clipboard |
||
852 | var hint = egw.lang('Clipboard') + ":\n[" + clipboard.type.join(',')+"]\n"; |
||
853 | paste_action.set_hint(hint); |
||
854 | // Add titles of entries |
||
855 | for(var i = 0; i < clipboard.selected.length; i++) |
||
856 | { |
||
857 | var id = clipboard.selected[i].id.split('::'); |
||
858 | egw.link_title(id[0],id[1],function(title) {if(title)this.hint += title+"\n";},paste_action); |
||
859 | } |
||
860 | |||
861 | // Add into links so it's included in menu |
||
862 | if(paste_action && paste_action.enabled.exec(paste_action, clipboard.selected, _selected[0])) |
||
863 | { |
||
864 | if(typeof _links[paste_action.id] == 'undefined') |
||
865 | { |
||
866 | _links[paste_action.id] = { |
||
867 | "actionObj": paste_action, |
||
868 | "enabled": false, |
||
869 | "visible": clipboard != null, |
||
870 | "cnt": 0 |
||
871 | }; |
||
872 | } |
||
873 | while(paste_action.children.length > 0) |
||
874 | { |
||
875 | paste_action.children[0].remove(); |
||
876 | } |
||
877 | |||
878 | // If nothing [valid] in the clipboard, don't bother with children |
||
879 | if(clipboard == null || typeof clipboard.type != 'object') |
||
880 | { |
||
881 | return; |
||
882 | } |
||
883 | |||
884 | // Add in actual actions as children |
||
885 | for(var k in drop) |
||
886 | { |
||
887 | // Add some choices - need to be a copy, or they interfere with |
||
888 | // the original |
||
889 | var drop_clone = jQuery.extend({},drop[k].actionObj); |
||
890 | var parent = paste_action.parent === drop_clone.parent ? paste_action : (paste_action.getActionById(drop_clone.parent.id) || paste_action); |
||
891 | drop_clone.parent = parent; |
||
892 | drop_clone.onExecute = new egwFnct(this, null, []); |
||
893 | drop_clone.children = []; |
||
894 | drop_clone.set_onExecute(paste_exec); |
||
895 | parent.children.push(drop_clone); |
||
896 | parent.allowOnMultiple = paste_action.allowOnMultiple && drop_clone.allowOnMultiple; |
||
897 | _links[k] = jQuery.extend({},drop[k]); |
||
898 | _links[k].actionObj = drop_clone; |
||
899 | |||
900 | // Drop is allowed if clipboard types intersect drop types |
||
901 | _links[k].enabled = false; |
||
902 | _links[k].visible = false; |
||
903 | for (var i = 0; i < drop_clone.acceptedTypes.length; i++) |
||
904 | { |
||
905 | if (clipboard.type.indexOf(drop_clone.acceptedTypes[i]) != -1) |
||
906 | { |
||
907 | _links[paste_action.id].enabled = true; |
||
908 | _links[k].enabled = true; |
||
909 | _links[k].visible = true; |
||
910 | break; |
||
911 | } |
||
912 | } |
||
913 | } |
||
914 | } |
||
915 | } |
||
916 | }; |
||
917 | return ai; |
||
918 | } |
||
919 | |||
922 |